{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a target=\"_blank\" href=\"https://colab.research.google.com/github/opensim-org/opensim-core/blob/main/Bindings/Python/tutorials/Tutorial%203%20-%20Loading%20and%20Modifying%20OpenSim%20Models.ipynb\">\n",
    "  <img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/>\n",
    "</a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1dzvRcZuYhsP"
   },
   "source": [
    "# 3. Loading and Modifying OpenSim Models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YLK6vcNo1NHP"
   },
   "source": [
    "## 3.1. Objectives"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TKiEOjfi1LW1"
   },
   "source": [
    "**Purpose**\n",
    "\n",
    "In this tutorial, you will learn to load and modify `Model`s in [OpenSim](https://opensim.stanford.edu/) [[1]](https://doi.org/10.1109/TBME.2007.901024), as well as to access and modify the data in those models. This tutorial will lead you through the following:\n",
    "\n",
    "*   Loading an existing `Model`.\n",
    "*   Accessing and modifying `Model` property.\n",
    "*   Saving a `Model`.\n",
    "\n",
    "**Format**\n",
    "\n",
    "You will first download a model and load it into OpenSim. Then, you will access and print information from the model. Finally, you will modify the model and save and download it to your computer. \n",
    "\n",
    "**Resources**\n",
    "\n",
    "All of the files necessary to complete this tutorial are available in the following Google Drive folder: https://drive.google.com/drive/folders/18wYREuiX0jWYEmZhfgL6OCMJMJDv86lJ?usp=sharing\n",
    "\n",
    "While you complete the tutorial, you will find cells that automatically download the required files.\n",
    "\n",
    "To obtain a link to one of the files in Google Drive, right-click over it and click on **Get Link**.\n",
    "\n",
    "**Local Installation**\n",
    "\n",
    "You may also run this notebook using a locally installed OpenSim `conda` package. Visit [the OpenSim python scripting Confluence page](https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python) for more information on how to install OpenSim in a local Python environment. You will also need to download the resources from Google Drive to the same directory as this notebook. Then, you may skip to Section 3.3 below."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ADllcxbsjTr8"
   },
   "source": [
    "## 3.2. Setup `conda` and OpenSim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "mmmTuEI4HQJG"
   },
   "source": [
    "First, set up the environment by executing the following cell (see [Tutorial 1: Introduction to OpenSim](https://colab.research.google.com/github/opensim-org/opensim-core/blob/main/Bindings/Python/tutorials/Tutorial%201%20-%20Introduction%20to%20OpenSim.ipynb) for more details)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "uYZPRpAQxXmp"
   },
   "outputs": [],
   "source": [
    "!pip install -q condacolab\n",
    "import condacolab\n",
    "condacolab.install()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vt5loGbdfKb8"
   },
   "source": [
    "Don't worry if after executing the previous cell you get an error saying that your session has failed. This happens because `condacolab` needs to restart the session for the changes to take effect. Therefore, you have to execute the previous cell before executing any other cell.\n",
    "\n",
    "Now, execute the following cell to install the OpenSim `conda` package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "6l59_J_U8ubr"
   },
   "outputs": [],
   "source": [
    "!conda install opensim-org::opensim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4gtTNdaO8wOI"
   },
   "source": [
    "## 3.3. Load an OpenSim `Model`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qQhpl9pUcgPf"
   },
   "source": [
    "In this section, you will download an OpenSim `Model` representing a double pendulum, load it into memory using the OpenSim API, and explore some of its properties."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TIQp3TtGYMyQ"
   },
   "source": [
    "First, let's download the model file (`double_pendulum.osim`). The file is stored in a Google Drive folder (See **Resources** in the Objectives section)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "LjMgzeHLYa2W"
   },
   "outputs": [],
   "source": [
    "!gdown \"1lxuQ7PI9aiv9Mr4G1xj31PuBn3i6HjJI&confirm=t\" # double_pendulum.osim"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_F6Hrnjba7vL"
   },
   "source": [
    "Now, let's import OpenSim, load the model, and print some metadata."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "fmGIZ0Hl8z_G"
   },
   "outputs": [],
   "source": [
    "import opensim as osim\n",
    "\n",
    "# Load the model.\n",
    "double_pendulum = osim.Model('double_pendulum.osim')\n",
    "\n",
    "# Print metadata.\n",
    "print(\"Name of the model:\", double_pendulum.getName())\n",
    "print(\"Author:\", double_pendulum.get_credits())\n",
    "print(\"Publications:\", double_pendulum.get_publications())\n",
    "print(\"Length Unit:\", double_pendulum.get_length_units())\n",
    "print(\"Force Unit:\", double_pendulum.get_force_units())\n",
    "print(\"Gravity:\", double_pendulum.get_gravity())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TQG_Qc9_NDsd"
   },
   "source": [
    "Let's explore some of the model's body properties. Since it is a double pendulum, we expect the model to have two bodies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "tSs587JhVu5j"
   },
   "outputs": [],
   "source": [
    "# Print the number of bodies.\n",
    "print(\"Num Bodies:\", double_pendulum.getNumBodies())\n",
    "print()\n",
    "\n",
    "# For each body, print some information, such as its name and mass.\n",
    "for body in double_pendulum.getBodySet():\n",
    "    print(\"Body Name:\", body.getName())\n",
    "    print(\"Body Absolute Path:\", body.getAbsolutePathString())\n",
    "    print(\"Body Mass:\", body.getMass())\n",
    "    print(\"Body Mass Center:\", body.getMassCenter())\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "v_xgSNv9Yz2m"
   },
   "source": [
    "Let's explore some of the model's joint properties. Again, we expect to have two joints. The first joint connects the ground to `rod1`, so the model does not fall when simulated. The second joint connects `rod1` to `rod2`, the two bodies of the double pendulum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "2laouerdY59s"
   },
   "outputs": [],
   "source": [
    "# Print the number of joints.\n",
    "print(\"Num Joints:\", double_pendulum.getNumJoints())\n",
    "print()\n",
    "\n",
    "# For each joint, print some information, such as its name or components that\n",
    "# this joint owns.\n",
    "for joint in double_pendulum.getJointSet():\n",
    "    print(\"Joint Name:\", joint.getName())\n",
    "    print(\"Joint Absolute Path:\", joint.getAbsolutePathString())\n",
    "    print(\"Components:\")\n",
    "    for component in joint.getComponentsList():\n",
    "        print(\"  Component Name:\", component.getName())\n",
    "        print(\"  Component Absolute Path:\", component.getAbsolutePathString())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "hW93EJzR9dFx"
   },
   "source": [
    "Finally, let's explore the coordinates of the model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "dj2PFUrD9gbF"
   },
   "outputs": [],
   "source": [
    "# Print the number of coordinates.\n",
    "print(\"Num Coordinates:\", double_pendulum.getNumCoordinates())\n",
    "print()\n",
    "\n",
    "# For each coordinate, print some information, such as its name or motion type.\n",
    "for coordinate in double_pendulum.getCoordinateSet():\n",
    "    print(\"  Coordinate Name:\", coordinate.getName())\n",
    "    print(\"  Coordinate Absolute Path:\", coordinate.getAbsolutePathString())\n",
    " \n",
    "    # Motion type is represented using an enum (0:Undefined, 1:Rotational, \n",
    "    # 2:Translational, 3:Coupled).\n",
    "    motion_type = coordinate.getMotionType()\n",
    "    motion_type_string = \"\"\n",
    "    if motion_type == 0:\n",
    "        motion_type_string = \"Undefined\"\n",
    "    elif motion_type == 1:\n",
    "        motion_type_string = \"Rotational\"\n",
    "    elif motion_type == 2:\n",
    "        motion_type_string = \"Translational\"\n",
    "    elif motion_type == 3:\n",
    "        motion_type_string = \"Coupled\"\n",
    "    print(\"  Coordinate Motion Type:\", motion_type_string)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "v_I5gYAdazmy"
   },
   "source": [
    "## 3.4. Modify the `Model`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "p_zHk2nBctVE"
   },
   "source": [
    "Let's modify some of the model's properties. For example, let's set the model's gravity to be gravity on the planet Mars (3.72 $m/s^2$), and double the mass of the bodies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Q72WxmqOa64r"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "# Set gravity to that of planet Mars (3.72 m/s^2).\n",
    "double_pendulum.set_gravity(osim.Vec3(0, -3.72, 0))\n",
    "print(\"New Gravity:\", double_pendulum.get_gravity())\n",
    "print()\n",
    "\n",
    "# For each body, print its name, set the mass as 2 kg, and print the updated mass.\n",
    "for body in double_pendulum.getBodyList():\n",
    "    print(\"Body Name:\", body.getName())\n",
    "    body.setMass(2.0)\n",
    "    print(\"Body New Mass:\", body.getMass())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "o5CJiJePnF-4"
   },
   "source": [
    "Now, let's save the model into an `osim` file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "gL0mAwSnbBNi"
   },
   "outputs": [],
   "source": [
    "double_pendulum.printToXML('heavy-double-pendulum-mars.osim');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "vpAWFZGHnXzH"
   },
   "source": [
    "The file can be downloaded in the Files pane onthe left by `right-clicking` on it and clicking `download` (if the file is not in the tree at the left, you may need to `right-click` and `refresh`).\n",
    "\n",
    "Alternatively, you can download the file programmatically as shown in the following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "YoBp15HOnrGV"
   },
   "outputs": [],
   "source": [
    "from google.colab import files\n",
    "\n",
    "files.download('heavy-double-pendulum-mars.osim') "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If working locally, the file will be saved to the current working directory."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "i7rhPT2id6gg"
   },
   "source": [
    "## 3.5. Conclusion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1XK9UgItWQMF"
   },
   "source": [
    "In this tutorial, you downloaded a double pendulum model and loaded it into OpenSim. Then, you explored and modified some of its properties and components. Finally, you saved the updated model into an `.osim` file."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "lLWgd8Noun6Y"
   },
   "source": [
    "## 3.6. Useful Links\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "m8M3dlT5xQcD"
   },
   "source": [
    "> **OpenSim Website:** https://opensim.stanford.edu/\n",
    ">\n",
    "> **OpenSim Python Scripting:** https://opensimconfluence.atlassian.net/wiki/spaces/OpenSim/pages/53085346/Scripting+in+Python\n",
    ">\n",
    "> **OpenSim API Documentation:** https://simtk.org/api_docs/opensim/api_docs/\n",
    "> \n",
    "> **OpenSim Creator Website:** https://opensimcreator.com/\n",
    "> \n",
    "> **SimTK Website:** https://simtk.org/projects/opensim\n",
    "> \n",
    "> **Biomechanics of Movement Course Videos:** https://www.youtube.com/channel/UCDNGy0KKNLQ-ztcL5h2Z6zA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rFR2Rf9tk_9A"
   },
   "source": [
    "## 3.7 Acknowledgments\n",
    "\n",
    "Thanks to [OpenSimColab](https://simtk.org/projects/opencolab) project [[2]](https://doi.org/10.1080/10255842.2022.2104607) for creating the first OpenSim Conda package."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dxMjdukAnH2N"
   },
   "source": [
    "## 3.8. References\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "WX2A_iaYulV1"
   },
   "source": [
    "> [1].   Delp, S. L., Anderson, F. C., Arnold, A. S., Loan, P., Habib, A., John, C. T., Guendelman, E., & Thelen, D. G. (2007). **OpenSim: open-source software to create and analyze dynamic simulations of movement.** *IEEE Transactions on Bio-Medical Engineering*, 54(11), 1940–1950. https://doi.org/10.1109/TBME.2007.901024\n",
    ">\n",
    "> [2] Mokhtarzadeh, H., Jiang, F., Zhao, S., & Malekipour, F. (2022). **OpenColab project: OpenSim in Google colaboratory to explore biomechanics on the web.** *Computer Methods in Biomechanics and Biomedical Engineering*, 1–9. https://doi.org/10.1080/10255842.2022.2104607"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "opensim",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
